package com.lfy.modules.blog.service.impl;

import cn.hutool.core.date.DateUtil;
import cn.hutool.crypto.digest.DigestUtil;
import cn.hutool.extra.spring.SpringUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.lfy.modules.ai.cache.AiDispatchCache;
import com.lfy.modules.common.config.sys.DictionariesKey;
import com.lfy.modules.common.config.sys.SysDictionaries;
import com.lfy.encrypt.EncryptUtil;
import com.lfy.modules.blog.cache.AuthCache;
import com.lfy.modules.blog.domain.*;
import com.lfy.modules.blog.domain.dto.user.PwdDTO;
import com.lfy.modules.blog.domain.dto.user.UserDTO;
import com.lfy.modules.blog.domain.dto.user.ModifyFreezeUserDTO;
import com.lfy.modules.blog.domain.dto.user.ModifyUserRoleDTO;
import com.lfy.modules.blog.domain.vo.auth.UserListVO;
import com.lfy.modules.blog.mapper.*;
import com.lfy.modules.blog.service.*;
import com.lfy.modules.common.custom.FileService;
import com.lfy.modules.common.custom.constants.AuthConstant;
import com.lfy.modules.common.custom.constants.FileTypeEnum;
import com.lfy.modules.common.custom.constants.UserEnum;
import com.lfy.modules.common.domain.PageResultVO;
import com.lfy.modules.common.domain.entity.UserEntity;
import com.lfy.modules.blog.domain.dto.LoginDTO;
import com.lfy.modules.blog.domain.vo.auth.DynamicRoutesVO;
import com.lfy.modules.blog.domain.vo.auth.LoginVO;
import com.lfy.modules.common.domain.BaseEnum;
import com.lfy.modules.common.domain.R;
import com.lfy.modules.common.intercept.AuthHandlerInterceptor;
import com.lfy.modules.common.utils.JsonUtils;
import com.lfy.modules.common.utils.JwtUtil;
import com.lfy.modules.common.utils.PageUtil;
import com.lfy.modules.common.utils.RequestUtil;
import com.lfy.modules.disk.domain.DiskFileEntity;
import com.lfy.modules.disk.domain.DiskShareDetailEntity;
import com.lfy.modules.disk.service.DiskFileService;
import com.lfy.modules.disk.service.DiskShareDetailService;
import com.lfy.modules.letter.domain.SiteLetterEntity;
import com.lfy.modules.letter.domain.builder.Letter;
import com.lfy.modules.letter.domain.builder.LetterEnum;
import com.lfy.modules.letter.domain.event.LetterEvent;
import com.lfy.modules.letter.service.SiteLetterService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.stream.Collectors;

/**
* @author 吕飞扬
* @description 针对表【user】的数据库操作Service实现
* @createDate 2024-04-03 01:13:44
*/
@Service
@Slf4j
public class UserServiceImpl extends ServiceImpl<UserEntityMapper, UserEntity>
    implements UserService {
    private static final Integer TOKEN_EXPIRE_TIME = 1000 * 60 * 60 * 6;
    private static final Integer FAST_TOKEN_EXPIRE_TIME = 1000 * 60 * 60;

    @Resource
    private UserEntityMapper userMapper;
    @Resource
    private RoleRouteContactService routeContactService;
    @Value("${rsa.privateKey}")
    private String privateKey;
    @Resource
    private RouteServiceImpl routeService;
    @Resource
    private RoleService roleService;
    @Resource
    private HomeDetailService homeDetailService;
    @Resource
    private ThreadPoolExecutor secondaryThreadPool;
    @Resource
    private BlogArticleTextMapper blogArticleTextMapper;
    @Resource
    private BlogArticleMapper articleMapper;
    @Resource
    private BlogArticleTagMapper articleTagMapper;
    @Resource
    private BlogArticleClassificationMapper classificationMapper;
    @Resource
    private ArticleCommentMapper commentMapper;
    @Resource
    private BlogMoodMapper moodMapper;

    @Resource
    private ThreadPoolExecutor mainThreadPool;
    @Resource
    private SiteLetterService letterService;
    @Resource
    private SocialService socialService;
    @Resource
    private DiskFileService diskFileService;
    @Resource
    private DiskShareDetailService diskShareDetailService;
    @Resource
    private FileService fileService;
    @Resource
    private AiDispatchCache aiDispatchCache;
    @Resource
    private FeedbackService feedbackService;


    @Override
    public R<LoginVO> login(LoginDTO loginDTO, HttpServletResponse response) {
        String userName = loginDTO.getUserName().trim();
        String decryptPassword = null;
        boolean flag = true;
        //解密密码
        try {
            decryptPassword =   EncryptUtil.decryptByPrivateKey(loginDTO.getPassword(),privateKey);
            flag = false;
        } catch (Exception e) {
            log.info("解密密码失败",e);
        }
        if(flag || StringUtils.isBlank(decryptPassword)){
            return R.error(BaseEnum.authEnum.LOGIN_ERROR);
        }

        UserEntity userEntity = this.userMapper.selectOne(new LambdaQueryWrapper<UserEntity>().eq(UserEntity::getUsername, userName));
        if(Objects.isNull(userEntity)){
            return R.error(BaseEnum.authEnum.LOGIN_ERROR);
        }
        String password = userEntity.getPassword().trim();
        if(userEntity.getIsFreeze().equals(UserEnum.IS_FREEZE.getCode())){
            return R.error(BaseEnum.BlackEnum.FREEZE);
        }
        if(!DigestUtil.bcryptCheck(decryptPassword,password) ){
            return R.error(BaseEnum.authEnum.LOGIN_ERROR);
        }

        String jwt = JwtUtil.createJwt(userEntity, TOKEN_EXPIRE_TIME);
        String fastJwt = JwtUtil.createJwt(userEntity, FAST_TOKEN_EXPIRE_TIME);
        LoginVO loginVO = new LoginVO();
        loginVO.setToken(jwt);
        loginVO.setFastToken(fastJwt);
        loginVO.setName(userEntity.getName());
        Long id = userEntity.getId();
        Long roleId = userEntity.getRole();
        RoleEntity role = this.roleService.getById(roleId);
        HomeDetail homeDetail = homeDetailService.getOne(new LambdaQueryWrapper<HomeDetail>().eq(HomeDetail::getTenantId, id));
        loginVO.setRoleName(role.getRoleName());
        loginVO.setRoleKey(role.getRoleKey());
        loginVO.setRoleLev(role.getRoleLev());
        Set<String> keys = AuthCache.getAuthKeysByUserId(userEntity.getRole());
        loginVO.setKeys(keys);
        loginVO.setLastLoginTime(DateUtil.formatDateTime(userEntity.getLastLogin()));
        if(Objects.nonNull(homeDetail)){
            String prefix = SysDictionaries.getValue(DictionariesKey.SYS_MINIO_SITE);
            loginVO.setHeadImgUrl(prefix+homeDetail.getAuthorHeaderImg());
            if(homeDetail.getAuthorName() != null){
                loginVO.setName(homeDetail.getAuthorName());
            }
        }
        secondaryThreadPool.execute(() -> {
            //更新用户最后登录时间
            UserEntity u = new UserEntity();
            u.setId(id);
            userEntity.setLastLogin(new Date());
            userMapper.updateById(userEntity);
        });
        return R.success(loginVO);
    }

    /**
     * 动态路由权限
     * @return
     */
    @Override
    public R<?> getRoute(UserEntity userEntity) {
        List<RoleRouteContactEntity> list = routeContactService.list(new LambdaQueryWrapper<RoleRouteContactEntity>().eq(RoleRouteContactEntity::getRoleId, userEntity.getRole()));
        if(CollectionUtils.isEmpty(list)){
            return R.success();
        }
        Set<Long> routeIds = list.stream().map(RoleRouteContactEntity::getRouteId).collect(Collectors.toSet());
        List<RouteEntity> routeEntities = (List<RouteEntity>) routeService.listByIds(routeIds);
        List<DynamicRoutesVO> routesVOS = routeEntities.stream()
                .map(item -> {
                    DynamicRoutesVO dynamicRoutesVO = new DynamicRoutesVO();
                    BeanUtils.copyProperties(item, dynamicRoutesVO);
                    return dynamicRoutesVO;
                }).collect(Collectors.toList());
        //根节点
        List<DynamicRoutesVO> root = routesVOS.stream().filter(item -> item.getParentId().equals(0L)).collect(Collectors.toList());
        for (DynamicRoutesVO dynamicRoutesVO : root) {
            dynamicRoutesVO.setChildren(treeBuild(dynamicRoutesVO,routesVOS));
        }

        return R.success(root.stream().filter(item-> !CollectionUtils.isEmpty(item.getChildren())).collect(Collectors.toList()));
    }

    @Override
    public R<PageResultVO<UserListVO>> getUserList(Integer page, Integer size,String name) {
        LambdaQueryWrapper<UserEntity> queryWrapper = new LambdaQueryWrapper<>();
        if(StringUtils.isNotEmpty(name)){
            queryWrapper.like(UserEntity::getUsername,name).or().like(UserEntity::getUrl,name).or().like(UserEntity::getName,name);
        }
        Page<UserEntity> userEntityPage = new Page<>(page, size);
        IPage<UserEntity> userEntityIPage = this.userMapper.selectPage(userEntityPage, queryWrapper);

        List<UserEntity> userEntities = userEntityPage.getRecords();
        if(CollectionUtils.isEmpty(userEntities)){
            return R.success(PageUtil.convert2PageEmptyResult(userEntityIPage, UserListVO.class));
        }
        List<RoleEntity> list = this.roleService.list(null);
        List<UserListVO> userListVOs = userEntities.stream().map((item) -> {
            UserListVO userListVO = new UserListVO();
            BeanUtils.copyProperties(item, userListVO);
            for (RoleEntity roleEntity : list) {
                if(item.getRole().equals(roleEntity.getId())){
                    UserListVO.UserRoleVO userRoleVO = new UserListVO.UserRoleVO();
                    userRoleVO.setId(roleEntity.getId());
                    userRoleVO.setRoleName(roleEntity.getRoleName());
                    userListVO.setRole(userRoleVO);
                }
            }
            userListVO.setCreateTime(DateUtil.formatDateTime(item.getCreateTime()));
            userListVO.setFreeze(item.getIsFreeze());
            return userListVO;
        }).collect(Collectors.toList());
        PageResultVO<UserListVO> userListVOPageResultVO = PageUtil.buildPageResultVO(userEntityIPage, userListVOs);
        return R.success(userListVOPageResultVO);
    }

    @Override
    public R<Void> modifyUserRoleByUserId(ModifyUserRoleDTO dto) {
        UserEntity userEntity = this.userMapper.selectById(dto.getUserId());
        if(Objects.isNull(userEntity)){
            return R.error(BaseEnum.authEnum.USER_NOT_EXIST);
        }
        Long userEntityRole = userEntity.getRole();
        Long adminRole = AuthHandlerInterceptor.authLocal.get().getRole();
        RoleEntity roleEntity = this.roleService.getById(userEntityRole);
        RoleEntity adminRoleEntity = this.roleService.getById(adminRole);
        if(!AuthConstant.ROOT_ROLE.equals(adminRoleEntity.getRoleKey()) &&roleEntity.getRoleLev() >= adminRoleEntity.getRoleLev()){
            return R.error(BaseEnum.authEnum.NO_AUTH);
        }
        userEntity.setRole(dto.getRoleId());

        this.userMapper.updateById(userEntity);
        mainThreadPool.execute(() -> {
            //发送角色变更通知
            RoleEntity role = this.roleService.getById(dto.getRoleId());
            SiteLetterEntity siteLetterEntity = Letter.builder(LetterEnum.LetterType.SYS_SEND.getCode(),
                    LetterEnum.SysSubjectType.ROLE_CHANGE_EVENT.getSubject(null),
                    Letter.builderChangeRoleContent(role.getRoleName()), userEntity.getId(), 0);
            SpringUtil.publishEvent(new LetterEvent(this, siteLetterEntity));
        });
        return R.success();
    }

    @Override
    public R<Void> freezeUser(ModifyFreezeUserDTO dto) {
        UserEntity userEntity = this.userMapper.selectById(dto.getId());

        if(Objects.isNull(userEntity)){
            return R.error(BaseEnum.authEnum.USER_NOT_EXIST);
        }
        Long role = userEntity.getRole();
        Long adminRole = AuthHandlerInterceptor.authLocal.get().getRole();
        RoleEntity roleEntity = this.roleService.getById(role);
        RoleEntity adminRoleEntity = this.roleService.getById(adminRole);
        if(!AuthConstant.ROOT_ROLE.equals(adminRoleEntity.getRoleKey()) &&roleEntity.getRoleLev() >= adminRoleEntity.getRoleLev()){
                    return R.error(BaseEnum.authEnum.NO_AUTH);
        }
        userEntity.setIsFreeze(dto.getIsFreeze());
        this.userMapper.updateById(userEntity);
        return R.success();

    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public R<Void> createUser(UserDTO dto) {
        UserEntity userEntity = new UserEntity();
        userEntity.setName(dto.getName());
        userEntity.setUsername(dto.getUsername());
        userEntity.setPassword(DigestUtil.bcrypt(dto.getPassword()));
        userEntity.setRole(dto.getRoleId());
        userEntity.setIsFreeze(dto.getFreeze());
        userEntity.setUrl(dto.getUrl());
        Long createId = AuthHandlerInterceptor.authLocal.get().getId();
        userEntity.setCreateBy(createId);
        String ip = RequestUtil.getIp();
        userEntity.setCreateIp(ip);
        userEntity.setCreateIpAddress(RequestUtil.getPlace(ip));
        this.userMapper.insert(userEntity);
        HomeDetail homeDetail = new HomeDetail();
        homeDetail.setTenantId(userEntity.getId());
        homeDetail.setAnnouncement("默认公告");
        homeDetail.setAuthorAge(0);
        homeDetail.setAuthorSex(1);
        homeDetail.setDynamicSign("默认");
        homeDetail.setSiteAuther("默认建站用户");

        homeDetail.setSiteTime("1999-01-01 00:00:00");
        homeDetail.setSign("默认签名");
        homeDetail.setDynamicSign("默认打字机签名");
        homeDetail.setAuthorName("默认用户名");
        homeDetail.setBlogTitle("无名氏");
        homeDetail.setBlogTitleEnglish("WUMINGSHI");
        this.homeDetailService.save(homeDetail);
        SocialEntity socialEntity = new SocialEntity();

        socialEntity.setTenantId(userEntity.getId());
        socialEntity.setAuthorGitee("https://github.com/wumingshen");
        socialEntity.setAuthorQq("1234567890");
        socialEntity.setAuthorWyy("7151839080");
        socialEntity.setAuthorWx("1234567890");
        socialEntity.setAuthorEmail(dto.getUsername());
        this.socialService.save(socialEntity);
        mainThreadPool.execute(() -> {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            aiDispatchCache.initUserCache();
        });
        return R.success();
    }

    @Override
    public R<Void> modifyUser(UserDTO dto) {
        UserEntity userEntity = new UserEntity();
        userEntity.setId(dto.getId());
        if(StringUtils.isNotBlank(dto.getName())){
            userEntity.setName(dto.getName().trim());
        }

        if(StringUtils.isNotBlank(dto.getUsername())){
            userEntity.setUsername(dto.getUsername().trim());
        }

        if(StringUtils.isNotBlank(dto.getPassword())){
            userEntity.setPassword(DigestUtil.bcrypt(dto.getPassword().trim()));
        }
            userEntity.setRole(dto.getRoleId());
            userEntity.setIsFreeze(dto.getFreeze());
        if(StringUtils.isNotBlank(dto.getUrl())){
            userEntity.setUrl(dto.getUrl().trim());
        }

        this.userMapper.updateById(userEntity);
        mainThreadPool.execute(() -> {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            aiDispatchCache.initUserCache();
        });
        return R.success();
    }

    @Override
    public R<Void> modifyPassword(PwdDTO encryptDTO, UserEntity userEntity) {
        String encryptedForm = encryptDTO.getEncryptedForm();
        String formStr = null;
        boolean flag = true;
        try {
             formStr = EncryptUtil.decryptByPrivateKey(encryptedForm, privateKey);
             flag = false;
        } catch (Exception e) {
            log.warn("com.lfy.modules.blog.service.impl.UserServiceImpl.modifyPassword()  解密失败",e);
        }
        if(flag || StringUtils.isBlank(formStr)){
            return R.error("密码解密失败");
        }
        PwdDTO.PwdForm dto = null;
        try {
            dto = JsonUtils.readValue(formStr, PwdDTO.PwdForm.class);
        } catch (JsonProcessingException e) {
            log.error("in com.lfy.modules.blog.service.impl.UserServiceImpl.modifyPassword() 转换失败",e);
        }
        if(Objects.isNull(dto)){
            return R.error("密码表单转换失败,请联系管理员");
        }
        if(StringUtils.isBlank(dto.getOldPassword()) || StringUtils.isBlank(dto.getNewPassword()) || StringUtils.isBlank(dto.getConfirmPassword())){
            return R.error("密码不能为空");
        }
        if(!dto.getNewPassword().trim().equals(dto.getConfirmPassword().trim())){
            return R.error("两次密码输入不一致");
        }
        if(dto.getNewPassword().trim().length() < 6 || dto.getNewPassword().trim().length() > 16){
            return R.error("密码长度必须在6-16位之间");
        }
        Long id = userEntity.getId();
        UserEntity user = this.userMapper.selectById(id);
        if(Objects.isNull(user)){
            return R.error(BaseEnum.authEnum.USER_NOT_EXIST);
        }
        if(!DigestUtil.bcryptCheck(dto.getOldPassword(),user.getPassword().trim())){
            return R.error();
        }
        user.setPassword(DigestUtil.bcrypt(dto.getNewPassword()));
        this.userMapper.updateById(user);
        return R.success();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public R<Void> deleteUser(Long userId) {
        UserEntity userEntity = this.userMapper.selectById(userId);
        if(Objects.isNull(userEntity)){
            return R.error(BaseEnum.authEnum.USER_NOT_EXIST);
        }
        UserEntity admin = AuthHandlerInterceptor.authLocal.get();
        Long deleteId = userEntity.getId();
        Long adminId = admin.getId();
        if(deleteId.equals(adminId)){
            return R.error(BaseEnum.authEnum.DELETE_SELF_ERROR);
        }
        Long role = userEntity.getRole();
        Long adminRole = admin.getRole();
        RoleEntity roleEntity = this.roleService.getById(role);
        RoleEntity adminRoleEntity = this.roleService.getById(adminRole);
        if(!AuthConstant.ROOT_ROLE.equals(adminRoleEntity.getRoleKey()) &&roleEntity.getRoleLev() >= adminRoleEntity.getRoleLev()){
            return R.error(BaseEnum.authEnum.NO_AUTH);
        }
        //删除用户及其相关信息
        this.removeById(userId);
        this.homeDetailService.remove(new LambdaQueryWrapper<HomeDetail>().eq(HomeDetail::getTenantId,userId));
        this.moodMapper.delete(new LambdaQueryWrapper<MoodEntity>().eq(MoodEntity::getTenantId,userId));
        List<ArticleEntity> articleIds = this.articleMapper.selectList(new LambdaQueryWrapper<ArticleEntity>().eq(ArticleEntity::getTenantId, userId).select(ArticleEntity::getId));
        if(!CollectionUtils.isEmpty(articleIds)){
            Set<Long> idSet = articleIds.stream().map(ArticleEntity::getId).collect(Collectors.toSet());
            this.commentMapper.delete(new LambdaQueryWrapper<ArticleComment>().in(ArticleComment::getArticleId,idSet));
        }

        this.articleMapper.delete(new LambdaQueryWrapper<ArticleEntity>().eq(ArticleEntity::getTenantId,userId));
        this.blogArticleTextMapper.delete(new LambdaQueryWrapper<ArticleTextEntity>().eq(ArticleTextEntity::getTenantId,userId));
        this.articleTagMapper.delete(new LambdaQueryWrapper<ArticleTagEntity>().eq(ArticleTagEntity::getTenantId,userId));
        this.classificationMapper.delete(new LambdaQueryWrapper<ArticleClassificationEntity>().eq(ArticleClassificationEntity::getTenantId,userId));
        this.letterService.remove(Wrappers.lambdaQuery(SiteLetterEntity.class).eq(SiteLetterEntity::getReceiveId,userId));
        this.socialService.remove(Wrappers.lambdaQuery(SocialEntity.class).eq(SocialEntity::getTenantId,userId));
        this.feedbackService.remove(Wrappers.lambdaQuery(FeedbackEntity.class).eq(FeedbackEntity::getTenantId,userId));

        mainThreadPool.execute(() -> {
            //删除网盘文件
            try{
                List<DiskFileEntity> list = this.diskFileService.list(Wrappers.lambdaQuery(DiskFileEntity.class).eq(DiskFileEntity::getTenantId, userId));
                if(!CollectionUtils.isEmpty(list)){
                    List<String> collect = list.stream().filter(item->!item.getType().equals(FileTypeEnum.FOLDER.getCode()))
                            .map(DiskFileEntity::getFilePath).filter(Objects::nonNull).collect(Collectors.toList());
                    fileService.removeFileByNames(collect);
                    Set<Long> set = list.stream().map(DiskFileEntity::getId).collect(Collectors.toSet());
                    this.diskShareDetailService.remove(Wrappers.lambdaQuery(DiskShareDetailEntity.class).in(DiskShareDetailEntity::getFileId,set));
                    this.diskFileService.removeByIds(set);
                }
            }catch (Exception e){
                log.error("删除网盘文件失败,原因:{},用户id:{}",e,userId);
                throw e;
            }});
        return R.success();
    }


    private List<DynamicRoutesVO> treeBuild(DynamicRoutesVO root,List<DynamicRoutesVO> list){
        List<DynamicRoutesVO> collect = list.stream().filter(item -> item.getParentId().equals(root.getId())).collect(Collectors.toList());
        for (DynamicRoutesVO dynamicRoutesVO : collect) {
            dynamicRoutesVO.setChildren(treeBuild(dynamicRoutesVO,list));
        }
        return collect;
    }

}




